package com.kob.backend.consumer.utils;

import com.alibaba.fastjson2.JSONObject;
import com.kob.backend.consumer.WebSocketServer;
import com.kob.backend.pojo.Bot;
import com.kob.backend.pojo.Record;
import com.kob.backend.pojo.User;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

public class Game extends Thread{
        private Integer L;
        private final Integer rows;
    private final Integer cols;
    private final Integer inner_wall_counts;

    private final int [][] g;

    private  final Player playerA,playerB;
    private Integer nextStepA = null;
    private Integer nextStepB = null;//4是stop

    private ReentrantLock lock = new ReentrantLock();
    private String status = "playing";//playing -> finished
    private String Loser = "all";//all,a,b,

    private List<Integer> food_x = new ArrayList<>();
    private List<Integer> food_y = new ArrayList<>();

    private final static String addBotUrl = "http://127.0.0.1:3032/bot/add/";


    public Game(Integer rows, Integer cols, Integer inner_wall_counts, Integer idA, Bot botA, Integer idB,Bot botB){
        this.rows = rows;
        this.cols = cols;
        this.inner_wall_counts = inner_wall_counts;

        this.g = new int[rows][cols];
        Integer botIdA = -1 , botIdB = -1;
        String botCodeA = "",botCodeB = "";
        if(botA != null){
            botIdA = botA.getId();
            botCodeA = botA.getContent();
        }
        if(botB != null){
            botIdB = botB.getId();
            botCodeB = botB.getContent();
        }

        playerA = new Player(idA,botIdA,botCodeA,rows-2,1,0,new ArrayList<>());
        playerB = new Player(idB,botIdB,botCodeB,1,cols-2,0,new ArrayList<>());


    }
    public Player getPlayerA(){
        return playerA;
    }
    public  Player getPlayerB(){
        return playerB;
    }
    public void setNextStepA(Integer nextStepA){
        lock.lock();
        try{
            this.nextStepA = nextStepA;
        }finally {
            lock.unlock();
        }

    }
    public void setNextStepB(Integer nextStepB){
        lock.lock();
        try {
            this.nextStepB = nextStepB;
        }finally {
            lock.unlock();
        }

    }


    public int[][] getG(){
        return g;
    }

    public boolean wallIsAlso(int sx, int sy, int tx, int ty) {
        if (sx == tx && sy == ty) {
            return true;
        }

        g[sx][sy] = 1;

        int[] dx = {-1, 0, 1, 0};
        int[] dy = {0, 1, 0, -1};

        for (int i = 0; i < 4; i++) {
            int x = sx + dx[i];
            int y = sy + dy[i];

            if (x >= 0 && x < g.length && y >= 0 && y < g[0].length
                    && g[x][y] == 0 && wallIsAlso(x, y, tx, ty)) {
                g[sx][sy] = 0;
                return true;
            }
        }
        g[sx][sy] = 0;
        return false;
    }

    private boolean draw() {//画地图
        for(int i =0;i<this.rows;i++)//初始化
        {
            for(int j = 0;j<this.cols;j++)
            {
                g[i][j] = 0;
            }
        }

            // 给四周加上墙
        for (int r = 0; r < rows; r++) {
            g[r][0] = g[r][cols - 1] = 1;
        }

        for (int c = 0; c < cols; c++) {
            g[0][c] = g[rows - 1][c] = 1;
        }

        Random random = new Random();
        // 创建随机障碍物
        for (int i = 0; i < inner_wall_counts; i++) {
            for (int j = 0; j < 1000; j++) {
                int r = random.nextInt(this.rows);
                int c = random.nextInt(this.cols);

                if (g[r][c] == 1 || g[this.rows - 1 - r][this.cols - 1 - c] == 1) {
                    continue;
                }
                if ((r == this.rows - 2 && c == 1) || (r == 1 && c == this.cols - 2)) {
                    continue;
                }

                g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = 1;
                break;
            }
        }

        return wallIsAlso(this.rows-2,1,1,this.cols-2);
    }

    public void createMap(){
        for(int i = 0; i < 1000 ;i++){
            if(draw())
                break;
        }
    }
    private void sendBotCode(Player player,String input){
        if(player.getBotId().equals(-1))return;//人来操作，不用执行代码
        MultiValueMap<String,String> data = new LinkedMultiValueMap<>();
        data.add("user_id",player.getId().toString());
        data.add("bot_id",player.getBotId().toString());
        data.add("bot_code", player.getBotCode());
        data.add("input",input);


        WebSocketServer.restTemplate.postForObject(addBotUrl,data,String.class);
    }


    private boolean nextStep(Integer userId){
        boolean logo = false;
        lock.lock();
        try {
            if(nextStepA != null && Objects.equals(userId, playerA.getId())) {
                playerA.getSteps().add(nextStepA);
                logo = true;
            }
            if(nextStepB != null && Objects.equals(playerB.getId(), userId)) {
                playerB.getSteps().add(nextStepB);
                logo = true;
            }
        }finally {
            lock.unlock();
        }
        return logo;
    }
    private void sendResult(){//同步游戏结果
        JSONObject resp = new JSONObject();
        resp.put("event","result");
        resp.put("loser",Loser);
        sendAllMessage(resp.toJSONString());
    }
    private void sendMove(Integer userId){//向两名玩家传递移动信息
        lock.lock();
        try {
            JSONObject resp = new JSONObject();
            resp.put("event","move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            resp.put("a_id",playerA.getId());
            resp.put("b_id",playerB.getId());
            sendAllMessage(resp.toJSONString());

            if(Objects.equals(userId, playerA.getId()))
                nextStepA = null;
            else if(Objects.equals(userId, playerB.getId()))
                nextStepB = null;
        }finally {
            lock.unlock();
        }

    }

    private void sendAllMessage(String message){
        if(WebSocketServer.users.get(playerA.getId()) != null)
        WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        if(WebSocketServer.users.get(playerB.getId()) != null)
        WebSocketServer.users.get(playerB.getId()).sendMessage(message);

    }
//    public void move(){
//        System.out.println("run了");
//        if(nextStep()){//判断是否有蛇需要改变方向
//            if(status.equals("playing")){
//                sendMove();
//            }else {
//                sendResult();
//            }
//        }
//    }
    private String getMapString(){
        StringBuilder res = new StringBuilder();
        for(int i = 0; i < rows;i++){
            for(int j = 0;j < cols;j++){
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }
    private String getFoodXString(){
        StringBuilder res = new StringBuilder();
        for(int i: food_x){
            res.append(i);
            res.append('#');
        }
        return res.toString();
    }
    private String getFoodYString(){
        StringBuilder res = new StringBuilder();
        for(int i: food_y){
            res.append(i);
            res.append('#');
        }
        return res.toString();
    }

    private void saveToDatabase(Integer cells){//结果保存到数据库里面

        Integer ratingA = WebSocketServer.userMapper.selectById(playerA.getId()).getRating();
        Integer ratingB = WebSocketServer.userMapper.selectById(playerB.getId()).getRating();

        if("a".equals(Loser)){
            ratingA -= 2;
            ratingB += cells+2;
        } else if ("b".equals(Loser)) {
            ratingA +=cells+2;
            ratingB -= 2;
        }
        updateUserRating(playerA,ratingA);
        updateUserRating(playerB,ratingB);

        Record record = new Record(
                null,
                playerA.getId(),
                playerB.getId(),
                playerA.getStepsString(),
                playerB.getStepsString(),
                getMapString(),
                getFoodXString(),
                getFoodYString(),
                Loser,
                new Date()

        );
        WebSocketServer.recordMapper.insert(record);
    }

    private void updateUserRating(Player player,Integer rating){
        User user = WebSocketServer.userMapper.selectById(player.getId());
        user.setRating(rating);

        WebSocketServer.userMapper.updateById(user);
    }

    public void die(Integer id,Integer cells){
        if(status.equals("over"))
        {
            return;
        }
        JSONObject resp = new JSONObject();
        resp.put("event","over");
        if(playerA.getId().equals(id)){
            Loser = "a";
            status = "over";
        } else if (playerB.getId().equals(id)) {
            Loser = "b";
            status = "over";
        }else return;
        resp.put("id",id);

        saveToDatabase(cells);//把对局记录保存在数据库中
        sendAllMessage(resp.toJSONString());
    }
    public void move(String map,Integer userId){
        if(nextStep(userId)){//判断是否有蛇需要改变方向
//            if(Objects.equals(userId, playerA.getId()))
//                System.out.println("wcnmm");
            if(status.equals("playing")){
                sendMove(userId);
                if(playerA.getBotId() != -1 && Objects.equals(playerA.getId(), userId))
                    sendBotCode(playerA,map);
                if(playerB.getBotId() != -1 && Objects.equals(playerB.getId(), userId))
                    sendBotCode(playerB,map);
            }else {
                sendResult();
            }
        }
        if(playerA.getSend_message() == 0 && playerA.getBotId() != -1 && Objects.equals(playerA.getId(), userId)){
            sendBotCode(playerA,map);
        }
        if(playerB.getSend_message() == 0 && playerB.getBotId() != -1 && Objects.equals(playerB.getId(), userId)){
            sendBotCode(playerB,map);
        }
        playerA.setSend_message(playerA.getSend_message() + 1);
        playerB.setSend_message(playerB.getSend_message() + 1);
    }
    @Override
    public void run() {
        Random random = new Random();
        for(int i=0;i<1000;i++)
        {
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(!Objects.equals(status, "playing"))return;

            int r = random.nextInt(rows);
            int c = random.nextInt(cols);
            food_x.add(r);
            food_y.add(c);
            JSONObject resp = new JSONObject();
            resp.put("event","food");
            resp.put("r",r);
            resp.put("c",c);
            sendAllMessage(resp.toJSONString());
        }
    }
}
